#include "Worker.h"
#include "Zipper/Zipper.h"
#include "SingletonHolder.h"
#include <boost/filesystem.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/optional.hpp>



Worker::Worker(PComunicator::DataContainer *theContainer,Validator::Validator *theValidator,std::string theAdress,std::string thePort,std::string theDirectory) : myClient(new Client(theAdress,thePort))
{
	myContainer= theContainer;
	myValidator = theValidator;
	myDirectory = theDirectory;
	myAllOK = false;

	myClient->Connect();
}


bool Worker::IsOK()
{
	return myAllOK;
}


void Worker::operator()()
{
	std::vector<PComunicator::PFileInfo> aReceivedFiles = myContainer->GetContainer<PComunicator::PFileInfo>();

	if(aReceivedFiles.size() < 1)
	{
		myAllOK = false;
		return;
	}
	
	std::vector<std::string> aStringData = myContainer->GetContainer<std::string>();
	
	//z stranky sa textove udaje posielaju v poradi : username, realname, predmet, semester
	//preto najprv je potrebne skontrolovat ci sa naozaj poslali 3 textove udaje
	if(aStringData.size() != 4)
	{
		SingletonHolder::Instance().WriteMsg("Username, Realname, Subject or Semester has not been received.Cant continue...");
		return;
	}

	//dalej zistime ci existuje validacna schema pre zadany predmet
	//ak taka schema nexistuje neda sa pokracovat
	boost::optional<Validator::Scheme> selectedScheme = myValidator->GetScheme(aStringData[2]);
	if(selectedScheme.is_initialized())
	{
		myScheme = selectedScheme.get();
	}
	else
	{
		SingletonHolder::Instance().WriteEx("No validation scheme available for: "+aStringData[2]+" please first create one.");
		SingletonHolder::Instance().WriteMsg("Quiting Worker...");
		return;
	}

	//ulozime zadanie do adresara ktory je tvoreny zakladnym adresarom + predmetom + loginom
	std::string zip_location = write_data(aReceivedFiles[0],aStringData[0],aStringData[2]);

	Zipper aZipper(aReceivedFiles[0]); //predpokladame ze na vstup, teda z web rozhrania stale pride len jeden subor !
	Validator::Scheme aZipScheme = aZipper.GetScheme();

	if(myScheme == aZipScheme)
	{
		std::vector<Validator::MetaFile> aFiles = myScheme.GetFilesToExtract();
		std::vector<boost::shared_ptr<PComunicator::PFileInfo> > aExtractedFiles;

		for(std::size_t i=0; i < aFiles.size(); i++)
		{
			if(aFiles[i].DoExtract())
			{
                PComunicator::PFileInfo *aExtracted = NULL;
				if(aZipper.ExtractNoType(aFiles[i].GetName(),&aExtracted))
				{
					aExtractedFiles.push_back(boost::shared_ptr<PComunicator::PFileInfo>(aExtracted));
				}
			}
		}
		
		send_data(aExtractedFiles,zip_location,aStringData[2]);		
	}
	else
	{
		myAllOK = false;
		SingletonHolder::Instance().WriteMsg("The scheme for the received data doesnt match the predefined scheme!");
	}

	delete myContainer;
}



void Worker::send_data(std::vector<boost::shared_ptr<PComunicator::PFileInfo> > &theInput,
																				std::string theZipPath,
																				std::string predmet)
{
	if(myClient->IsConnected())
	{
		std::vector<std::string> aStringData = myContainer->GetContainer<std::string>();
		PComunicator::DataContainer new_container;

		new_container.AddCommand("convert",false);

		//pridame data ktore sme prijali (z web stranky)
		for(std::size_t i = 0; i < aStringData.size(); i++)
		{
			new_container.AddData(aStringData[i]);
		}
		
		//new_container.AddData(predmet);			//pridame info o predmete pre ktory sa robi kontrola <<!!!OBSOLETE!!!
		new_container.AddData(theZipPath);		//pridame cestu k zipu

		//pridame rozbalene subory
		for (std::size_t i =0; i < theInput.size(); i++)
		{
			new_container.AddData((*theInput[i]));
		}

		myClient->Write(new_container);
		myAllOK = true;
	}

	myAllOK = false;
}


std::string Worker::write_data(PComunicator::PFileInfo theFile,std::string theUserLogin,std::string theSubject)
{
	boost::mutex aMutex;
	boost::mutex::scoped_lock aLock(aMutex);

	try
	{
		//vytvorime adresar kam sa ulozi subor 
		std::string new_dir = myDirectory + theSubject +"/" + theUserLogin +"/";
		boost::filesystem::create_directories(new_dir);

		//ulozime subor do vytvoreneho adresara
		theFile.Save(new_dir);

		return new_dir+theFile.GetFullName();
	}
	catch(const boost::filesystem::filesystem_error &ex)
	{
		SingletonHolder::Instance().WriteEx(ex.what());
		return theFile.GetFullName();
	}

}